#include "project_config.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "qelib.h"
#include "stdout.h"
#if (SYSLOG_ENABLE == 1)
#include "syslog.h"
#endif

QELOG_DOMAIN("shell");

typedef struct
{
	qe_uint size;
} rx_event;

static qe_shell shell;
static qe_dev *uart_dev         = QE_NULL;
static TaskHandle_t task_handle = QE_NULL;
static QueueHandle_t rx_msgq    = QE_NULL;



static const char shell_show_info[] = 
    "Project Name      : "PROJECT_NAME"\r\n"
    "Project Version   : "PROJECT_VERSION"\r\n"
    "Project Build     : "__DATE__" "__TIME__"\r\n";

static qe_ret rx_indicate(qe_dev *dev, qe_size size)
{
	rx_event event;
    BaseType_t woken = pdFALSE;
    qe_assert(rx_msgq != QE_NULL);
    event.size = size;
    xQueueSendFromISR(rx_msgq, &event, &woken);
    portYIELD_FROM_ISR(woken);
    return qe_ok;
}

static void shell_log_output(const char *message, int len, void *user)
{
    qe_shell_write_end_line(&shell, (char *)message, qe_strlen(message));
}

void shell_write_end_line(char *message, int len)
{
    qe_shell_write_end_line(&shell, (char *)message, qe_strlen(message));
}

static int shell_read(qe_shell *sh, char *data, int len)
{
    char event;

    if (pdPASS == xQueueReceive(rx_msgq, &event, portMAX_DELAY)) {
        return qe_dev_read(uart_dev, 0, data, len);
    }
    return 0;
}

static int shell_write(qe_shell *sh, char *data, int len)
{
    return qe_dev_write(uart_dev, 0, data, len);
}

static void shell_stdout(char c, void *priv)
{
    qe_dev_write(uart_dev, 0, &c, 1);
}

static void shell_task(void *args)
{
	int n;
    char data;
    rx_event event;

    rx_msgq = xQueueCreate(64, sizeof(rx_event));
    qe_assert(rx_msgq != QE_NULL);

    uart_dev = qe_dev_find(SHELL_UART_DEVICE);
    qe_assert(uart_dev != QE_NULL);

    /* set uart rx callback and open it */
    qe_dev_set_rx_indicate(uart_dev, rx_indicate);
    qe_dev_open(uart_dev, QE_DEV_F_INT_RX);

    /* clear fifo when open uart dev */
    do {
    	qe_msleep(10);
    } while ((n = qe_dev_read(uart_dev, 0, &data, 1)) > 0);

    /* init shell */
    qe_shell_init(&shell, 1024, SHELL_USER, shell_read, shell_write);
    qe_shell_set_usr_info(&shell, shell_show_info);

    qe_info("shell running");

#if (SYSLOG_ENABLE == 1)
    syslog_initdone_wait();
    syslog_add_output_handle("shell", shell_log_output, QE_NULL);
#endif

    while (1) {
    
        if (pdPASS == xQueueReceive(rx_msgq, &event, portMAX_DELAY)) {
    	    while (event.size--) {
                n = qe_dev_read(uart_dev, 0, &data, 1);
                if (n > 0) {
                    qe_set_stdout(shell_stdout, QE_NULL);
                    qe_shell_handler(&shell, data);
                }
    	    }
        }
    }
}

static qe_init int shell_init(void)
{
    BaseType_t ret = xTaskCreate(shell_task, "shell", 0x1000, QE_NULL, 2, &task_handle);
    if (ret != pdPASS) {
        qe_error("shell task create error:%d", ret);
        return -1;
    }

    return 0;
}
QE_APP_EXPORT(shell_init);

void shell_clear_buffer()
{
    int n;
    char data;

    if (!uart_dev)
        return;

    do {
        n = qe_dev_read(uart_dev, 0, &data, 1);
    } while (n > 0);
}

QE_SHELL_EXPORT_USR(SHELL_USER, SHELL_PASSWORD, slink user);
